﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Dynamic;
using System.Collections;

namespace DynamicToSql
{
    public sealed class Entity : DynamicObject, IEnumerable<KeyValuePair<string, Object>>
    {
        private IDictionary<string, object> properties;

        public Entity()
        {
            this.properties = new Dictionary<string, object>();
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            return properties.TryGetValue(binder.Name, out result);
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            properties[binder.Name] = value;

            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return this.properties.Keys;
        }

        public bool ContainsProperty(string name)
        {
            return properties.ContainsKey(name);
        }

        public IEnumerable<KeyValuePair<string, object>> GetProperties()
        {
            return properties.AsEnumerable();
        }

        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
        {
            return properties.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public object this[string property]
        {
            get
            {
                if (properties.ContainsKey(property))
                    return properties[property];
                else
                    return null;
            }
            set
            {
                properties[property] = value;
            }
        }

        public static Property Property(string name)
        {
            return new Property(name);
        }

        public static T ToType<T>(Entity instance)
        {
            return ToType<T>(instance, null);
        }

        public static T ToType<T>(Entity instance, PropertySelector selector)
        {
            T obj = Activator.CreateInstance<T>();
            foreach (var p in typeof(T).GetProperties())
            {
                if (selector == null)
                {
                    p.SetValue(obj, instance[p.Name], null);
                }
                else if (selector.IncludeProperties.Count > 0)
                {
                    if(selector.IncludeProperties.Contains(p.Name))
                        p.SetValue(obj, instance[p.Name], null);
                }
                else if (selector.ExcludeProperties.Count > 0)
                {
                    if (!selector.ExcludeProperties.Contains(p.Name))
                        p.SetValue(obj, instance[p.Name], null);
                }
            }
            return obj;
        }

        public static Entity ToEntity<T>(T instance)
        {
            return ToEntity<T>(instance, null);
        }

        public static Entity ToEntity<T>(T instance, PropertySelector selector)
        {
            var entity = new Entity();
            foreach (var p in instance.GetType().GetProperties())
            {
                if (selector == null)
                {
                    entity[p.Name] = p.GetValue(instance, null);
                }
                else if (selector.IncludeProperties.Count > 0)
                {
                    if (selector.IncludeProperties.Contains(p.Name))
                        entity[p.Name] = p.GetValue(instance, null);
                }
                else if (selector.ExcludeProperties.Count > 0)
                {
                    if (!selector.ExcludeProperties.Contains(p.Name))
                        entity[p.Name] = p.GetValue(instance, null);
                }
            }
            return entity;
        }
    }
}
